//
// Created by Chen qin lang on 2018/7/6.
//



#include "WlAudio.h"

WlAudio::WlAudio(int streamIndex, WlPlayStat *playStat, int sampleRate, JavaCall *call) :
        streamIndex(streamIndex), sampleRate(sampleRate), playStat(playStat) {
    this->call = call;
    queue = new WlQueue(playStat);
    buffer = (uint8_t *) av_malloc(sampleRate * 2 * 2);
    sampleBuff = (SAMPLETYPE *) malloc(sampleRate * 2 * 2);

    soundTouch = new SoundTouch;
    soundTouch->setSampleRate(sampleRate);
    soundTouch->setChannels(2);
    out_buffer = (uint8_t *) av_malloc(sampleRate * 2 * 2);
    //  soundTouch->setPitch(1.5f);
    //soundTouch->setTempo(1.5f);
}

void getQueueCallBack(SLAndroidSimpleBufferQueueItf context, void *pAudio) {
    WlAudio *wlAudio = (WlAudio *) pAudio;
    if (wlAudio != NULL) {
        int buffersize = wlAudio->getSoundData();
        wlAudio->clock += buffersize / (double) (wlAudio->sampleRate * 2 * 2);

        if (wlAudio->clock - wlAudio->last_time > 0.2/* &&
            wlAudio->duration + 0.1 >= wlAudio->clock*/) {
            wlAudio->last_time = wlAudio->clock;
            wlAudio->call->callTimeInfo(CHILD_THREAD, wlAudio->clock, wlAudio->duration);
        }

        (*wlAudio->pcmBufferQueue)->Enqueue(wlAudio->pcmBufferQueue, wlAudio->sampleBuff,
                                            buffersize * 2 * 2);
    }
}

void WlAudio::initOPELES() {
    SLresult result;
    result = slCreateEngine(&engineObject, 0, 0, 0, 0, 0);
    result = (*engineObject)->Realize(engineObject, SL_BOOLEAN_FALSE);
    result = (*engineObject)->GetInterface(engineObject, SL_IID_ENGINE, &engineEngine);

    //第二步，创建混音器
    const SLInterfaceID mids[1] = {SL_IID_ENVIRONMENTALREVERB};
    const SLboolean mreq[1] = {SL_BOOLEAN_FALSE};
    result = (*engineEngine)->CreateOutputMix(engineEngine, &outputMixObject, 1, mids, mreq);
    (void) result;
    result = (*outputMixObject)->Realize(outputMixObject, SL_BOOLEAN_FALSE);
    (void) result;
    result = (*outputMixObject)->GetInterface(outputMixObject, SL_IID_ENVIRONMENTALREVERB,
                                              &outputMixEnvironmentalReverb);
    if (SL_RESULT_SUCCESS == result) {
        result = (*outputMixEnvironmentalReverb)->SetEnvironmentalReverbProperties(
                outputMixEnvironmentalReverb, &reverbSettings);
        (void) result;
    }
    SLDataLocator_OutputMix outputMix = {SL_DATALOCATOR_OUTPUTMIX, outputMixObject};
    SLDataSink audioSnk = {&outputMix, 0};


    // 第三步，配置PCM格式信息
    SLDataLocator_AndroidSimpleBufferQueue android_queue = {SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE,
                                                            2};

    SLDataFormat_PCM pcm = {
            SL_DATAFORMAT_PCM,//播放pcm格式的数据
            2,//2个声道（立体声）
            getSLSampleRate(),//44100hz的频率
            SL_PCMSAMPLEFORMAT_FIXED_16,//位数 16位
            SL_PCMSAMPLEFORMAT_FIXED_16,//和位数一致就行
            SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT,//立体声（前左前右）
            SL_BYTEORDER_LITTLEENDIAN//结束标志
    };
    SLDataSource slDataSource = {&android_queue, &pcm};


    const SLInterfaceID ids[4] = {SL_IID_BUFFERQUEUE, SL_IID_EFFECTSEND, SL_IID_VOLUME,SL_IID_PLAYBACKRATE};
    const SLboolean req[4] = {SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE,SL_BOOLEAN_TRUE};

    result = (*engineEngine)->CreateAudioPlayer(engineEngine, &pcmPlayerObject, &slDataSource,
                                                &audioSnk, 4, ids, req);
    //初始化播放器
    (*pcmPlayerObject)->Realize(pcmPlayerObject, SL_BOOLEAN_FALSE);

//    得到接口后调用  获取Player接口
    (*pcmPlayerObject)->GetInterface(pcmPlayerObject, SL_IID_PLAY, &pcmPlayerPlay);

//    注册回调缓冲区 获取缓冲队列接口
    (*pcmPlayerObject)->GetInterface(pcmPlayerObject, SL_IID_BUFFERQUEUE, &pcmBufferQueue);
    //注册声道控制器
    (*pcmPlayerObject)->GetInterface(pcmPlayerObject, SL_IID_MUTESOLO, &pcmPlayerMute);
    //缓冲接口回调
    (*pcmBufferQueue)->RegisterCallback(pcmBufferQueue, getQueueCallBack, this);
//    获取音量接口
    (*pcmPlayerObject)->GetInterface(pcmPlayerObject, SL_IID_VOLUME, &pcmPlayerVolume);
    setVolume(volumeProcess);
//    获取播放状态接口
    (*pcmPlayerPlay)->SetPlayState(pcmPlayerPlay, SL_PLAYSTATE_PLAYING);
    getQueueCallBack(pcmBufferQueue, this);
    LOGE("initopensl 2");
}

void *decodeThread(void *context) {
    WlAudio *audio = (WlAudio *) context;
    audio->initOPELES();
    pthread_exit(&audio->play_thread);
}

void WlAudio::play() {
    pthread_create(&play_thread, NULL, decodeThread, this);

}

int WlAudio::getSoundData() {
    while (playStat != NULL && !playStat->isexit) {
        out_buffer = NULL;
        if (finished) {
            finished = false;
            dataSize = resampleAudio(reinterpret_cast<void **>(&out_buffer));
            if (dataSize > 0) {
                for (int i = 0; i < dataSize / 2 + 1; i++) {
                    sampleBuff[i] = (out_buffer[i * 2] | ((out_buffer[i * 2 + 1]) << 8));
                }
                soundTouch->putSamples(sampleBuff, nb);
                num = soundTouch->receiveSamples(sampleBuff, dataSize / 4);
            } else {
                soundTouch->flush();
            }

        }
        if (num == 0) {
            finished = true;
            continue;
        } else {
            if (out_buffer == NULL) {
                num = soundTouch->receiveSamples(sampleBuff, dataSize / 4);
                if (num == 0) {
                    finished = true;
                    continue;
                }
            }
            return num;
        }

    }
    return 0;
}

int WlAudio::resampleAudio(void **out_buffer) {
    while (queue->playStat != NULL && !queue->playStat->isexit) {
        if (queue->playStat->seek) {
            av_usleep(1000 * 100);
            continue;
        }
        if (queue->getAvpacketSize() > 0) {
            if (playStat->load) {
                playStat->load = false;
                call->onCallLoad(CHILD_THREAD, false);
            }
            AVPacket *avPacket = av_packet_alloc();
            if (queue->getAvpacket(avPacket) != 0) {
                av_packet_free(&avPacket);
                av_free(avPacket);
                avPacket = NULL;
                continue;
            }
            int ret = avcodec_send_packet(codecCtx, avPacket);
            if (ret < 0) {
                av_packet_free(&avPacket);
                av_free(avPacket);
                LOGE("avcodec_send_packet_failed %s", av_err2str(ret));
                break;
            }
            AVFrame *avFrame = av_frame_alloc();
            ret = avcodec_receive_frame(codecCtx, avFrame);
            if (ret < 0) {
                av_frame_free(&avFrame);
                av_free(avFrame);
                avFrame = NULL;
                av_packet_free(&avPacket);
                av_free(avPacket);
                avPacket = NULL;
                LOGE("avcodec_receive_frame_failed %s", av_err2str(ret));
                continue;
            }
            //校验声道数
            if (avFrame->channel_layout == 0 && avFrame->channels > 0) {
                avFrame->channel_layout = av_get_default_channel_layout(avFrame->channels);
            }
            if (avFrame->channel_layout > 0 && avFrame->channels == 0) {
                avFrame->channels = av_get_channel_layout_nb_channels(avFrame->channel_layout);
            }
            SwrContext *swrContext = NULL;
            swrContext = swr_alloc_set_opts(NULL, AV_CH_LAYOUT_STEREO,
                                            AV_SAMPLE_FMT_S16,
                                            avFrame->sample_rate,
                                            avFrame->channel_layout,
                                            (AVSampleFormat) avFrame->format, avFrame->sample_rate,
                                            NULL, NULL
            );
            if (!swrContext || swr_init(swrContext) < 0) {
                av_frame_free(&avFrame);
                av_free(avFrame);
                avFrame = NULL;
                av_packet_free(&avPacket);
                av_free(avPacket);
                avPacket = NULL;
                LOGE("swrContext_init_failed");
                continue;
            }
            nb = swr_convert(swrContext, &buffer,/*  (uint8_t **) buffer 不能使用强转*/
                             avFrame->nb_samples,
                             (const uint8_t **) avFrame->data,
                             avFrame->nb_samples);
            if (nb < 0) {
                av_frame_free(&avFrame);
                av_packet_free(&avPacket);
                av_free(avPacket);
                swr_free(&swrContext);
                swrContext = NULL;
                LOGE("swr_convert_failed %s", av_err2str(ret));
                continue;
            }
            int out_channes = av_get_channel_layout_nb_channels(AV_CH_LAYOUT_STEREO);
            dataSize = nb * out_channes * av_get_bytes_per_sample(AV_SAMPLE_FMT_S16);
            now_time = avFrame->pts * (double) av_q2d(timebase);
            if (now_time < clock) {
                now_time = clock;
            }
            clock = now_time;
            *out_buffer = buffer;
            av_frame_free(&avFrame);
            av_free(avFrame);
            avFrame = NULL;
            av_packet_free(&avPacket);
            av_free(avPacket);
            avPacket = NULL;
            swr_free(&swrContext);
            swrContext = NULL;
            break;

        } else {
            if (!playStat->load) {
                playStat->load = true;
                call->onCallLoad(CHILD_THREAD, true);
            }
            av_usleep(1000 * 100);
            continue;
        }
    }
    return dataSize;
}

SLuint32 WlAudio::getSLSampleRate() {
    switch (sampleRate) {
        case 8000:
            return SL_SAMPLINGRATE_8;
        case 11025:
            return SL_SAMPLINGRATE_11_025;
        case 12000:
            return SL_SAMPLINGRATE_12;
        case 16000:
            return SL_SAMPLINGRATE_16;
        case 22050:
            return SL_SAMPLINGRATE_22_05;
        case 24000:
            return SL_SAMPLINGRATE_24;
        case 32000:
            return SL_SAMPLINGRATE_32;
        case 44100:
            return SL_SAMPLINGRATE_44_1;
        case 48000:
            return SL_SAMPLINGRATE_48;
        case 64000:
            return SL_SAMPLINGRATE_64;
        case 88200:
            return SL_SAMPLINGRATE_88_2;
        case 96000:
            return SL_SAMPLINGRATE_96;
        case 192000:
            return SL_SAMPLINGRATE_192;
        default:
            return SL_SAMPLINGRATE_44_1;
    }
}

void WlAudio::resume() {
    if (pcmPlayerPlay != NULL) {
        (*pcmPlayerPlay)->SetPlayState(pcmPlayerPlay, SL_PLAYSTATE_PLAYING);

    }
}

void WlAudio::pause() {
    if (pcmPlayerPlay != NULL) {
        (*pcmPlayerPlay)->SetPlayState(pcmPlayerPlay, SL_PLAYSTATE_PAUSED);

    }
}

WlAudio::~WlAudio() {

}

void WlAudio::release() {
    stop();
    if (queue != NULL) {
        delete (queue);
        queue = NULL;
    }

    if (pcmPlayerObject != NULL) {
        (*pcmPlayerObject)->Destroy(pcmPlayerObject);
        pcmPlayerObject = NULL;
        pcmPlayerPlay = NULL;
        pcmPlayerVolume = NULL;
        pcmPlayerMute = NULL;
        pcmPlayerVolume = NULL;
    }
    if (outputMixObject != NULL) {
        (*outputMixObject)->Destroy(outputMixObject);
        outputMixObject = NULL;
        outputMixEnvironmentalReverb = NULL;
    }
    if (engineObject != NULL) {
        (*engineObject)->Destroy(engineObject);
        engineObject = NULL;
        engineEngine = NULL;

    }
    if (buffer != NULL) {
        free(buffer);
    }
    if (out_buffer != NULL) {
        out_buffer = NULL;
    }
    if (soundTouch != NULL) {
        delete soundTouch;
        soundTouch = NULL;
    }
    if (sampleBuff != NULL) {
        free(sampleBuff);
        sampleBuff = NULL;
    }

    if (codecCtx != NULL) {
        avcodec_close(codecCtx);
        avcodec_free_context(&codecCtx);
        codecCtx = NULL;

    }
    if (call != NULL) {
        call = NULL;
    }
    if (playStat != NULL) {
        playStat = NULL;
    }
}

void WlAudio::stop() {
    if (pcmPlayerPlay != NULL) {
        (*pcmPlayerPlay)->SetPlayState(pcmPlayerPlay, SL_PLAYSTATE_STOPPED);

    }
}

void WlAudio::setVolume(int process) {
    volumeProcess = process;
    if (pcmPlayerVolume != NULL) {
        if (process > 30) {
            (*pcmPlayerVolume)->SetVolumeLevel(pcmPlayerVolume, (100 - process) * -20);
        } else if (process > 25) {
            (*pcmPlayerVolume)->SetVolumeLevel(pcmPlayerVolume, (100 - process) * -22);
        } else if (process > 20) {
            (*pcmPlayerVolume)->SetVolumeLevel(pcmPlayerVolume, (100 - process) * -25);

        } else if (process > 15) {
            (*pcmPlayerVolume)->SetVolumeLevel(pcmPlayerVolume, (100 - process) * -28);
        } else if (process > 10) {
            (*pcmPlayerVolume)->SetVolumeLevel(pcmPlayerVolume, (100 - process) * -30);

        } else if (process > 5) {
            (*pcmPlayerVolume)->SetVolumeLevel(pcmPlayerVolume, (100 - process) * -34);
        } else if (process > 3) {
            (*pcmPlayerVolume)->SetVolumeLevel(pcmPlayerVolume, (100 - process) * -37);
        } else if (process > 0) {
            (*pcmPlayerVolume)->SetVolumeLevel(pcmPlayerVolume, (100 - process) * -40);
        } else {
            (*pcmPlayerVolume)->SetVolumeLevel(pcmPlayerVolume, (100 - process) * -100);
        }

    }
}

void WlAudio::setMuter(int channels) {
    if (pcmPlayerMute != NULL) {
        if (channels == 0) {
            (*pcmPlayerMute)->SetChannelMute(pcmPlayerMute, 0, true);
            (*pcmPlayerMute)->SetChannelMute(pcmPlayerMute, 1, false);
        } else if (channels == 1) {
            (*pcmPlayerMute)->SetChannelMute(pcmPlayerMute, 0, false);
            (*pcmPlayerMute)->SetChannelMute(pcmPlayerMute, 1, true);
        } else if (channels == 2) {
            (*pcmPlayerMute)->SetChannelMute(pcmPlayerMute, 0, false);
            (*pcmPlayerMute)->SetChannelMute(pcmPlayerMute, 1, false);
        }

    }
}

void WlAudio::setPitch(float pitch) {
    if (soundTouch != NULL) {
        soundTouch->setPitch(pitch);
    }
}

void WlAudio::setTempo(float tempo) {
    if (soundTouch != NULL) {
        soundTouch->setTempo(tempo);
    }
}





